package com.silverhaze.aurora.launcher.ui;

import com.silverhaze.aurora.launcher.data.FileEntity;
import com.silverhaze.aurora.launcher.resources.Resources;
import com.silverhaze.aurora.launcher.util.UIHelper;
import com.silverhaze.aurora.launcher.web.WebDownloader;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;
import java.util.Properties;
import java.util.concurrent.Executors;

/**
 * This code is part of Kg.Axion project. Treat it as strictly confidential.
 * Created by Ivan Babanin.
 * Creation date: 4/7/11 7:53 PM
 */
public class MainFrame extends JFrame {

    public static final int BUFFER_SIZE = 1024;
    private static final String MAIN_LABEL_FORMAT = "Загрузка %d/%d (%s)...";
    private static final String LOAD_LABEL_FORMAT = "Загрузка %d/%d Кб...";

    private JLabel mainLabel;
    private JLabel loadLabel;

    private JProgressBar filesProgressBar;
    private JProgressBar fileLoadProgressBar;

    private JButton helpButton;
    private JButton continueButton;
    private JButton exitButton;

    private int currentFile;
    private double currentLoadCapacity;
    private boolean loadComplete;

    private String updateServer;

    public MainFrame() throws HeadlessException {
        Properties properties = new Properties();

        try {
            properties.load(new FileReader("server.properties"));
            updateServer = "http://" + properties.getProperty("server") + "/";
        } catch (IOException e) {
            e.printStackTrace();
        }

        String dataDirectoryString = System.getProperty("user.dir") + "\\data";
        File dataDirectory = new File(dataDirectoryString);

        if (!dataDirectory.exists()) {
            if (!dataDirectory.mkdir()) {
                error("<html> Невозможно создать директорию <b>/data</b> для хранения настроек приложения. " +
                        "<br>Пожалуйста создайте директорию вручную.", MainFrame.this);
            }
        }


        initializeControls();
        initializeView();

        if (updateServer != null) {
            launchUpdater();
        } else {
            filesProgressBar.setIndeterminate(false);
            continueButton.setEnabled(false);

            mainLabel.setText("<html>Отсуствует файл с параметрами (<b>server.properties</b>)");
            mainLabel.setForeground(Color.RED);
        }
    }

    private void launchUpdater() {
        loadComplete = false;

        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                String libDirectoryString = System.getProperty("user.dir") + "\\lib";
                File libDirectory = new File(libDirectoryString);

                if (!libDirectory.exists()) {
                    if (!libDirectory.mkdir()) {
                        error("<html> Невозможно создать директорию <b>/lib</b> для системных библиотек. " +
                                "<br>Пожалуйста создайте директорию вручную.", MainFrame.this);
                    }
                }

                if (libDirectory.isDirectory()) {
                    java.util.List<FileEntity> remoteFiles = null;
                    java.util.List<File> localFiles = new ArrayList<File>(Arrays.asList(libDirectory.listFiles()));

                    try {
                        remoteFiles = WebDownloader.listFileEntities(updateServer + "/updater");
                    } catch (Exception e) {
                        loadComplete = true;

                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                filesProgressBar.setIndeterminate(false);
                                mainLabel.setText("Невозможно установить соединение с сервером");
                            }
                        });
                    }

                    if (remoteFiles != null) {
                        ListIterator<File> localFileIterator = localFiles.listIterator();

                        java.util.List<File> toDelete = new ArrayList<File>();
                        final java.util.List<FileEntity> toLoad = new ArrayList<FileEntity>();

                        while (localFileIterator.hasNext()) {
                            File localFile = localFileIterator.next();
                            ListIterator<FileEntity> remoteFileIterator = remoteFiles.listIterator();

                            while (remoteFileIterator.hasNext()) {
                                FileEntity remoteFile = remoteFileIterator.next();

                                if (remoteFile.getFileName().equals(localFile.getAbsolutePath().substring(libDirectoryString.length() + 1))) {
                                    localFileIterator.remove();
                                    remoteFileIterator.remove();

                                    String localFileHash = getMD5Checksum(localFile);

                                    if (!localFileHash.equals(remoteFile.getFileHash())) {
                                        toLoad.add(remoteFile);
                                    }
                                }
                            }
                        }

                        toLoad.addAll(remoteFiles);
                        toDelete.addAll(localFiles);

                        for (File file : toDelete) {
                            if (!file.delete()) {
                                System.err.println("Can't delete file: " + file.getAbsolutePath());
                            }
                        }

                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                filesProgressBar.setIndeterminate(false);
                                filesProgressBar.setMaximum(toLoad.size());
                            }
                        });

                        currentFile = 1;
                        boolean success = true;
                        for (final FileEntity fileEntity : toLoad) {
                            byte[] fileEntityContent = null;

                            final int fileSize = fileEntity.getFileSize() / 1024;

                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    mainLabel.setText(String.format(MAIN_LABEL_FORMAT, currentFile, toLoad.size(), fileEntity.getFileName()));
                                    fileLoadProgressBar.setMaximum(fileSize);
                                }
                            });

                            try {
                                URL url = new URL(updateServer + "updater?fileName=" + fileEntity.getFileName());

                                int readByteSize = 0;
                                currentLoadCapacity = 0.0D;
                                byte[] buffer = new byte[BUFFER_SIZE];

                                InputStream inputStream = url.openStream();
                                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

                                while ((readByteSize = inputStream.read(buffer)) != -1) {
                                    outputStream.write(buffer, 0, readByteSize);

                                    currentLoadCapacity += (1.0 * readByteSize / BUFFER_SIZE);
                                    SwingUtilities.invokeLater(new Runnable() {
                                        @Override
                                        public void run() {
                                            fileLoadProgressBar.setValue((int) currentLoadCapacity);
                                            loadLabel.setText(String.format(LOAD_LABEL_FORMAT, (int) currentLoadCapacity, fileSize));
                                        }
                                    });
                                }

                                fileEntityContent = outputStream.toByteArray();
                            } catch (MalformedURLException e) {
                                e.printStackTrace();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                            if (fileEntityContent != null) {
                                try {
                                    FileOutputStream outputStream = new FileOutputStream(libDirectory + "\\" + fileEntity.getFileName());
                                    outputStream.write(fileEntityContent);
                                    outputStream.close();

                                    currentFile++;

                                    SwingUtilities.invokeLater(new Runnable() {
                                        @Override
                                        public void run() {
                                            filesProgressBar.setValue(currentFile);
                                        }
                                    });
                                } catch (FileNotFoundException e) {
                                    e.printStackTrace();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                loadLabel.setText("Ошибка загрузки: ");
                                success = false;
                            }
                        }

                        continueButton.setEnabled(success);

                        if (success) {
                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    loadLabel.setText("");

                                    if (toLoad.size() > 0) {
                                        mainLabel.setText("Обновление завершено");
                                    } else {
                                        mainLabel.setText("Обновление не требуется");
                                    }
                                }
                            });

                            loadComplete = true;
                        } else {
                            mainLabel.setText("Ошибка: проверьте сетевое соединение");
                        }
                    }
                }
            }
        });
    }

    private void initializeControls() {
        filesProgressBar = new JProgressBar();
        fileLoadProgressBar = new JProgressBar();

        mainLabel = new JLabel("Проверка на наличие обновлений...");
        loadLabel = new JLabel();

        helpButton = new JButton("Помощь");
        continueButton = new JButton(new AbstractAction("Продолжить") {
            @Override
            public void actionPerformed(ActionEvent e) {
                File libDirectory = new File("lib");
                final StringBuilder builder = new StringBuilder();
                final String jrePath = System.getProperty("user.dir") + "\\jre\\bin\\javaw.exe";
                String launchCommand = "cmd /c START " + spaceScreening(jrePath);

                builder.append(" -classpath ").append(
                        "jre\\lib\\alt-rt.jar;" +
                                "jre\\lib\\alt-string.jar;" +
                                "jre\\lib\\charsets.jar;" +
                                "jre\\lib\\deploy.jar;" +
                                "jre\\lib\\javaws.jar;" +
                                "jre\\lib\\jce.jar;" +
                                "jre\\lib\\jsse.jar;" +
                                "jre\\lib\\management-agent.jar;" +
                                "jre\\lib\\plugin.jar;" +
                                "jre\\lib\\resources.jar;" +
                                "jre\\lib\\rt.jar;" +
                                "jre\\lib\\ext\\dnsns.jar;" +
                                "jre\\lib\\ext\\localedata.jar;" +
                                "jre\\lib\\ext\\sunjce_provider.jar;" +
                                "jre\\lib\\ext\\sunmscapi.jar;" +
                                "jre\\lib\\ext\\sunpkcs11.jar");

                for (File libFile : libDirectory.listFiles()) {
                    builder.append("lib\\").append(libFile.getName()).append(";");
                }

                builder.append(" com.silverhaze.diamond.client.Application");

                System.out.println(launchCommand + builder.toString());


                ProcessBuilder processBuilder = new ProcessBuilder();
                processBuilder.command(launchCommand, builder.toString());
                Executors.newSingleThreadExecutor().execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Runtime.getRuntime().exec("cmd /c START " + spaceScreening(jrePath) + " " + builder.toString());
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                    }
                });

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }

                System.exit(0);
            }
        });
        exitButton = new JButton(new AbstractAction("Выход") {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (loadComplete) {
                    System.exit(0);
                } else {
                    if (confirm("Выход", "Обновление не завершено. Вы действительно хотите выйти?", JOptionPane.YES_NO_OPTION, MainFrame.this)) {
                        System.exit(0);
                    }
                }
            }
        });

//        continueButton.setEnabled(false);

        filesProgressBar.setIndeterminate(true);
    }

    private void initializeView() {
        setResizable(false);
        setTitle("Aurora - Обновление");
        setIconImage(Resources.ICON.getImage());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        getRootPane().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        JLabel image = new JLabel(Resources.IMAGE);

        add(image, BorderLayout.CENTER);
        add(
                UIHelper.vBox(
                        5,
                        UIHelper.packInPanel(mainLabel, BorderLayout.LINE_START),
                        5,
                        filesProgressBar,
                        5,
                        UIHelper.packInPanel(loadLabel, BorderLayout.LINE_START),
                        5,
                        fileLoadProgressBar,
                        10,
                        UIHelper.createBorderLayoutPanel(
                                new JComponent[]{
                                        helpButton,
                                        UIHelper.hBox(continueButton, 2, exitButton)
                                },
                                new Object[]{
                                        BorderLayout.LINE_START,
                                        BorderLayout.LINE_END
                                }
                        )
                ), BorderLayout.SOUTH
        );

        pack();
        setLocationRelativeTo(null);
    }


    public static byte[] createChecksum(File file) throws Exception {
        InputStream fis = new FileInputStream(file);

        int numRead;
        byte[] buffer = new byte[1024];
        MessageDigest complete = MessageDigest.getInstance("MD5");

        do {
            numRead = fis.read(buffer);

            if (numRead > 0) {
                complete.update(buffer, 0, numRead);
            }
        } while (numRead != -1);

        fis.close();

        return complete.digest();
    }

    public static String getMD5Checksum(File file) {
        StringBuilder result = new StringBuilder();

        try {
            byte[] b = createChecksum(file);

            for (int i = 0; i < b.length; i++) {
                result.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result.toString();
    }

    public static boolean confirm(String title, String message, int option, Component parent) {
        return JOptionPane.showConfirmDialog(parent, message, title, option, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION;
    }

    public static void error(String message, Component parent) {
        JOptionPane.showMessageDialog(parent, message, "Ошибка", JOptionPane.ERROR_MESSAGE);
    }

    public static void message(String title, String message, Component parent) {
        JOptionPane.showMessageDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
    }

    public static void warning(String message, Component parent) {
        JOptionPane.showMessageDialog(parent, message, "Внимание", JOptionPane.WARNING_MESSAGE);
    }

    public static void view(JComponent component, String title, Component parent) {
        JOptionPane.showMessageDialog(parent, component, title, JOptionPane.PLAIN_MESSAGE);
    }

    public static String spaceScreening(String filePath) {
        StringBuilder builder = new StringBuilder();
        String[] path = filePath.split("[\\\\]");

        String fileSeparator = System.getProperty("file.separator");
        for (int i = 0; i < path.length - 1; i++) {
            String string = path[i];

            if (string.contains(" ")) {
                string = "\"" + string + "\"";
            }

            builder.append(string).append(fileSeparator);
        }

        builder.append(path[path.length - 1]);
        return builder.toString();
    }
}
